#include <assert.h>
#include <stdio.h>
#include "clist.h"
#include "safe_malloc.h"

clist_t* new_clist()
{
  clist_t* tmp = (clist_t*)safe_malloc(sizeof(clist_t));
  tmp->front = NULL;
  tmp->end = NULL;
  tmp->size = 0;
  return tmp;
}

void clist_push_back(clist_t* list, void* data)
{
  assert(list != NULL);
  cnode_t* node = (cnode_t*)safe_malloc(sizeof(cnode_t));
  node->next = NULL;
  node->data = data;

  /*empty list case*/
  if(list->size == 0) {
    list->front = node;
    list->end = node;
  }
  else {
    list->end->next = node;
    list->end = node;
  }

  list->size++;
}

void clist_push_front(clist_t* list, void* data)
{
  assert(list != NULL);
  cnode_t* node = (cnode_t*)safe_malloc(sizeof(cnode_t));
  node->next = list->front;
  node->data = data;

  /*empty case*/
  if(list->size == 0) {
    list->front = node;
    list->end = node;
  }
  else {
    list->front = node;
  }
  list->size++;
}

void* clist_pop_back(clist_t* list)
{
  assert(list != NULL);

  cnode_t* node = list->end;

  if(list->size == 0) {
    return NULL;
  }
  else if(list->size == 1) {
    list->front = NULL;
    list->end = NULL;
  }
  else {
    cnode_t* i;
    for(i = list->front; i->next != node; i = i->next);
    /*i points to second to last*/
    i->next = NULL;
    list->end = i;
  }
  list->size--;
  assert(node);
  void* data = node->data;
  free(node);

  return data;
}

void* clist_pop_front(clist_t* list)
{
  assert(list != NULL);
  cnode_t* node = list->front;

  if(list->size == 0) {
    return NULL;
  }
  else if(list->size == 1) {
    list->front = NULL;
    list->end = NULL;
  }
  else {
    list->front = list->front->next;
  }
  list->size--;
  void* data = node->data;
  free(node);

  return data;
}

void* clist_remove(clist_t* list, int index)
{
  assert(list != NULL);
  assert(index > -1);
  assert(index < list->size);

  cnode_t* node = list->front;

  if(list->size == 0) {
    return NULL;
  }
  else if(list->size == 1) {
    list->front = NULL;
    list->end = NULL;
  }
  else {
    cnode_t* prev = NULL;
    for( ; index != 0; index--) {
      prev = node;
      node = node->next;
    }

    if(prev != NULL) {
      prev->next = node->next;
    }
    else { /*first element*/
      list->front = node->next;
    }
  }
  list->size--;
  void* d = node->data;
  free(node);
  return d;
}

void clist_reverse(clist_t* list)
{
  assert(list != NULL);

  if(list->front == NULL) return;

  cnode_t* new_front = list->end;
  cnode_t* new_end = list->front;

  cnode_t* q1 = NULL;
  cnode_t* q2 = list->front;
  cnode_t* q3 = list->front->next;
  while(q3 != NULL) {
    q2->next = q1;
    q1 = q2;
    q2 = q3;
    q3 = q3->next;
  }
  q2->next = q1;

  list->front = new_front;
  list->end = new_end;
}

int clist_size(clist_t* list)
{
  return list->size;
}

void clist_free(clist_t* list)
{
  cnode_t* tmp = list->front;
  while(tmp) {
    cnode_t* next = tmp->next;
    free(tmp);
    tmp = next;
  }
  free(list);
}

